/*
 * CPU.cpp
 *
 *  Created on: 2013-11-12
 *      Author: james
 *  Modified by:
 *  	Author: all
 */

#include "CPU.h"
#include "BlockedQ.h"
#include <iostream>
#include <unistd.h>

CPU::CPU(int timeQuantum, int cpuID) {
	CPU::cpuID = cpuID;
	CPU::currentProcessRuntime = 0;
	CPU::process = NULL;
	CPU::timeQuantum = timeQuantum;
	CPU::timeCounter = 0;
	CPU::totalCpuLIfetime = 0;
	CPU::runningTime = 0;
	prioQ = PriorityQ::getInstance();
}

CPU::~CPU() {

}

void CPU::run() {
	printf("CPU running\n");
	CPUSpawnTime = clock();
	while(true) {
		printf("CPU%d: getting process from STS\n", cpuID);
		addProcessToCPU(prioQ->pull());
	}
}

bool CPU::isCpuBound() {
	return process->getCurrentOperationType() == Process::CPU;
}

bool CPU::isIOBound() {
	return process->getCurrentOperationType() == Process::IO;
}

bool CPU::hasWorkLeft() {
	return process->getCurrentOperationTime() > 0;
}

void CPU::tick() {
	displayOperationSummary();
	currentProcessRuntime++;
	process->increasePriorityRuntime();
	process->decreaseCurrentOperationTime();
	usleep(MICROSECONDS_SLEEP_TIME);
}

void CPU::moveToNextOperation() {
	process->setNextOperation();
}

void CPU::runProcess() {

	while ( currentProcessRuntime < timeQuantum ) {
		if ( isCpuBound() && hasWorkLeft() ) {
			tick();
		} else if ( isCpuBound() && !hasWorkLeft() ) {

			process->setNextOperation();
			if ( process->isFinished() ) {
				pushToPriorityQ();
				return;
			}

		} else if (isIOBound()) {
			process->stopTimer(Process::CPUOperation);
			stopCpuTimer();
			BlockedQ::getInstance()->add(process);
			process = NULL;
			break;
		}
	}

	if ( process != NULL && currentProcessRuntime >= timeQuantum) {
		pushToPriorityQ();
	}
}

void CPU::addProcessToCPU(Process *process) {
	CPU::process = process;
	process->startTimer();
	startCpuTimer();
	currentProcessRuntime = 0;
	runProcess();
}

void CPU::pushToPriorityQ() {
	process->stopTimer(Process::CPUOperation);
	stopCpuTimer();
	printf("CPU%d : Adding Process%d to priorityQ\n",this->cpuID, process->getProcessID());
	prioQ->add(process);// add to readyQ
}

int CPU::getCpuID() {
	return cpuID;
}

void CPU::calculateTotalCpuLifetime() {
	totalCpuLIfetime = ( ( std::clock() - CPUSpawnTime ) / (double)CLOCKS_PER_SEC ) * 100;
}

void CPU::startCpuTimer() {
	startTime = clock();
}

void CPU::stopCpuTimer() {
	runningTime += ( ( std::clock() - startTime ) / (double)CLOCKS_PER_SEC ) * 100;
}

void CPU::displayOperationSummary() {
	printf("CPU%d: Process  : %d at Operation : %d with %d time remaining\n",
			cpuID, process->getProcessID(), process->getOperationIndex(), process->getCurrentOperationTime());
}

void CPU::displayCpuSummary() {
	double CpuUtilization = ( runningTime / totalCpuLIfetime ) * 100;
	printf("\tCPU %d TotalRunningTime: %.2f ms Runtime on Processes: %.2f ms Utilization: %.1f%%\n",
			cpuID, totalCpuLIfetime, runningTime, CpuUtilization );
}
